#ifndef CODEDBLOCK_HH
#define CODEDBLOCK_HH 1

#define DEBUG 0
#define DEBUG_RANK 0
#define DEBUG_ACK 0
// print actual coefficients in logs
#define DEBUG_CB 0
#include "galoisfield.hh"
#include <click/config.h>
#include <click/string.hh>
#include <click/packet.hh>

CLICK_DECLS


#define massert(v,arg...) do{                             \
                            if(!(v)) click_chatter(arg);  \
                            assert(v);                    \
                          }while(0)



typedef uint8_t uchar;

/**
 * Finite Field operations for GF
 */

extern GaloisField<8> gf256;
static inline uchar __field_mul(uchar a, uchar b) { return gf256.mul(a,b); }
static inline uchar __field_inv(uchar a) { return gf256.inverse(a); }


//class Matrix;
class PartialGEMatrix;
//class FullGEMatrix;


class CodedBlock  {
protected:
    friend class FullGEMatrix;
    unsigned _k; // K = number of coefficients = number of bytes needed to store the coefficients

    uchar *_coeff; // the coefficients
    unsigned _first; // first non-0 coeff.
    unsigned _degree; // total non-0 coeff.

    // should be called after each coefficient update
    // update the value of _first and _degree
    void update() {
        unsigned i;
        for(i = 0; i < _k && !_coeff[i]; ++i);
        _first = i;
        _degree = 0;
        for(i = _first; i < _k; ++i) {
            if(_coeff[i])
                ++_degree;
        }
    }

    uint32_t _datasize; // NOTE: size of _data
    uchar *_data;
    uint32_t _pkt_id;
    // only the pointer is mutable here
    //mutable WritablePacket * _p; // where _data is stored

    uchar &operator[](int i) { return _coeff[i]; }

    bool _copy;
    //void setup(int k, int datasize, WritablePacket *p = 0);
public:
    //CodedBlock():_k(0), _coeff(0),_datasize(0),_data(0){}
    CodedBlock(uchar* coeff=0,uint32_t k=0,uchar* data=0,uint32_t size=0,uint32_t padding=0,bool copy=true){
    	set(coeff,k,data,size,padding,copy);
    }

    // copying coefficients => datasize=0
    CodedBlock(const CodedBlock &cb){
    	set(cb._coeff,cb._k,cb._data,cb._datasize,cb._copy);
    }

    ~CodedBlock();

    const uchar* get_data(){
    	return _data;
    }

    void set(uchar* coeff,uint32_t k,uchar* data,uint32_t size,uint32_t padding=0,bool copy=true){
    	delete[] _coeff;
    	if(_copy) delete[] _data;
    	if(coeff!=0){
			_k=k+padding;
			_coeff = new uchar[_k];
			memset(_coeff,0,_k);
			memcpy(_coeff, coeff, k);
			update();
		}
		if(data!=0){
			_datasize=size+padding;
			if(copy){
				_data = new uchar[_datasize];
				memset(_data,0,_datasize);
				*(uint16_t*)_data=size;
				memcpy(_data, data, size);
			}
			else _data=data;

		}
		_copy=copy;
    }

    //make coeff with size=coeffsize,all zero,but set the "first" to 1
    void make_coeff(int coeffsize,int first){
    	_k=coeffsize;
    	_coeff = new uchar[_k];
    	memset(_coeff,0,_k);
    	_coeff[first]=1;
    	update();
    }

    void add(const CodedBlock &cb, uchar coeff);
    void div(uchar coeff);
    void add_all(const PartialGEMatrix &m);
    void normalize() { div(_coeff[_first]); }

    int operator[](int i) const { return _coeff[i]; }

    String print() const;
    int degree() const { return _degree; }
    int first() const { return _first; }
//    const uchar *data() const { return _data; }
//    const uchar *coeff() const { return _coeff; }
    int datasize() const { return _datasize; }
    int k() const { return _k; }

    static inline int random_range(int min, int max) {
        return min + ((random() ^ (random() >> 8)) % (max-min+1));
    }
};



CLICK_ENDDECLS

#endif //CODEDBLOCK_HH
